package com.yms.web;
import com.yms.spring.bean.BeanFactor;
import com.yms.web.annotation.MyController;
import com.yms.web.annotation.MyRequestMapping;
import com.yms.web.annotation.MyResponseBody;
import com.yms.web.annotation.MyAutowired;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

public class Myservlet extends HttpServlet {

    private static String  COMPENT_SCAN_ELEMENT_PACKAGE_NAME= "package";
    private static String COMPENT_SCAN_ELEMENT_NAME = "compentScan";
    private static String XML_PATH_LOCAL= "xmlPathLocal";
    /**
     * 视图前缀
     */
    private  static String prefix = "";
    /**
     * 视图后缀
     */
    private  static String suffix = "";
    private static String projectPath = Myservlet.class.getResource("/").getPath();
    private  static Map<String,Method> methodMap = new HashMap<>();
    private  static Map<String,Method> fieldMap = new HashMap<>();

    /**
     * Tomcat容器会根据web.xml中的配置，执行init方法（类需要继承HttpServlet）
     * 第一步：通过tomcat解析web.xml的方式，获取xmlPathLocal对应的自定义XML。
     * 第二步：解析自定义的xml（MyMVC.xml）
     * 第三步：根据MyMVC.xml，设置的规则扫描controller和视图文件，并将对应信息写入methodMap中
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
//        自定义配置bean的配置xml文件
        BeanFactor.createBean();
        // 进行url转义会将空格变成%20，所以需要转化回来
        projectPath = projectPath.replaceAll("%20"," ");
        String initParameter = config.getInitParameter(XML_PATH_LOCAL);
        // 解析xml文件 file:xml 文件对象
        String projectWebPath = projectPath.replaceAll("/classes", "");
        File file = new File(projectWebPath + "//" + initParameter);
        // 使用dom4j解析XML
        Document prase = prase(file);
        Element rootElement = prase.getRootElement();
        Element view = rootElement.element("view");
        prefix = view.attribute("prefix").getValue();
        suffix = view.attribute("suffix").getValue();
        Element compentScanEle = rootElement.element(COMPENT_SCAN_ELEMENT_NAME);
        String value = compentScanEle.attribute(COMPENT_SCAN_ELEMENT_PACKAGE_NAME).getValue();
        value = value.replaceAll("\\.", "/");
        // 根据解析后信息，扫描controller和视图文件
        scanProjectByPath(projectPath+value);
    }

    /**
     * 扫描文件
     * @param path
     */
    public void scanProjectByPath(String path){
        File file =new File(path);
        //递归解析项目所有文件
        scanFile(file);
    }

    /**
     * 递归扫描
     * @param file
     */
    public void scanFile(File file){
        //递归解析项目
        if (file.isDirectory()){
            for (File file1 : file.listFiles()) {
                scanFile(file1);
            }
        }else{
            String filePath =  file.getPath();
            String suffix =filePath.substring(filePath.lastIndexOf("."));
            if (suffix.equals(".class")){
                String classPath  =  filePath.replace(new File(projectPath).getPath()+"\\","");
                classPath = classPath.replaceAll("\\\\",".");
//                获取到了类的全限定路径名称
                String className = classPath.substring(0,classPath.lastIndexOf("."));
                try {
                    Class<?> clazz = Class.forName(className);
                    if (clazz.isAnnotationPresent(MyController.class)) {
                        MyRequestMapping classRequestMapping = clazz.getAnnotation(MyRequestMapping.class);
                        String classRequestMappingUrl = "";
                        if (classRequestMapping!=null){
                            classRequestMappingUrl = classRequestMapping.value();
                        }
                        for (Method method : clazz.getDeclaredMethods()) {
                            if (!method.isSynthetic()) {
                                MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                                if (annotation != null) {
                                    String methodRequsetMappingUrl  = "";
                                    methodRequsetMappingUrl  = annotation.value();
                                    System.out.println("类:"+clazz.getName()+"的"+method.getName()+"方法被映射到了"+classRequestMappingUrl+methodRequsetMappingUrl+"上面");
                                    methodMap.put(classRequestMappingUrl+methodRequsetMappingUrl,method);
                                }

                            }
                        }
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 通过dom4j 将文件变成对象
     * @param file
     * @return
     */
    public Document prase(File file){
        SAXReader saxReader = new SAXReader();
        try {
            return saxReader.read(file);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    /**
     * 获取请求，然后转发到对应controller中
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //拿到请求的URI
        String contextPath = ":"+req.getServerPort()+req.getContextPath();
        String path = req.getRequestURL().toString();
        int temp = path.indexOf(contextPath)+contextPath.length()+1;
        String substring = path.substring(temp);
        Method method = methodMap.get(substring);
        if (method!=null){
            //jdk8以前 直接拿参数名称 拿不到
            Parameter[] parameters = method.getParameters();
            Object[] objects = new Object[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                String name = parameter.getName();
                Class type = parameter.getType();
                if (type.equals(String.class)){
                    objects[i] = req.getParameter(name);
                }else if(type.equals(HttpServletRequest.class)){
                    objects[i] = req;
                }else if(type.equals(HttpServletResponse.class)){
                    objects[i] = resp;
                }else{
                    try {
                        Object o = type.newInstance();
                        for (Field field : type.getDeclaredFields()) {
                            field.setAccessible(true);
                            String fieldName = field.getName();
                            field.set(o,req.getParameter(fieldName));
                        }
                        objects[i] = o;
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

            try {
                Object o= null;
                o = method.getDeclaringClass().newInstance();
//              注入bean
                Field[] declaredFields = method.getDeclaringClass().getDeclaredFields();
                for (Field field :declaredFields){
                    MyAutowired annotation = field.getAnnotation(MyAutowired.class);
                    if(annotation!=null){
                        //表明需要注入
                        Class<?> type = field.getType();
                        Object bean = BeanFactor.getBean(type);
                        if(bean==null){
                            throw new Exception("没有这样的bean");
                        }
                        field.setAccessible(true);
                        field.set(o, bean);
                    }
                }
//              执行方法
                Object invoke = method.invoke(o, objects);
                // 判断返回值是否是Void
                if (!method.getReturnType().equals(Void.class)){
                    MyResponseBody annotation = method.getAnnotation(MyResponseBody.class);
                    if (annotation!=null){
                        //提供接口来做这个事情
                        resp.getWriter().write(String.valueOf(invoke));
                    }else {
                        // 返回视图信息
                        req.getRequestDispatcher(prefix+String.valueOf(invoke)+suffix).forward(req,resp);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else {
            resp.setStatus(404);
        }
    }
}